home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 May / PCpro_2006_05.ISO / files / mobile / fma-2.0-stable-setup.exe / {app} / source / uSIMEdit.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2004-09-10  |  46.5 KB  |  1,470 lines

  1. unit uSIMEdit;
  2.  
  3. {
  4. *******************************************************************************
  5. * Descriptions: SIM Card Phonebook Edit Implementation
  6. * $Source: /cvsroot/fma/fma/uSIMEdit.pas,v $
  7. * $Locker:  $
  8. *
  9. * Todo:
  10. *
  11. * Change Log:
  12. * $Log: uSIMEdit.pas,v $
  13. * Revision 1.20.2.1  2004/09/10 12:12:56  z_stoichev
  14. * - Fixed 998626 "/O" added to Contact names.
  15. *
  16. * Revision 1.20  2004/08/27 20:42:00  lordlarry
  17. * Merge from newxml
  18. *
  19. * Revision 1.19.4.1  2004/08/25 15:41:13  merijnb
  20. * added export to xml for SIM entries
  21. *
  22. * Revision 1.19  2004/07/01 14:38:41  z_stoichev
  23. * Remember columns sort and order.
  24. *
  25. * Revision 1.18  2004/06/24 14:56:03  z_stoichev
  26. * - Fixed Upload Contacts phone type to SIM issues.
  27. * - Fixed Max field length for ME and SM Phonebooks.
  28. * - Fixed Contact name lookup routines use ME phonebook.
  29. * - Changed Connect after unknown number call is entered.
  30. * - Changed Explorer Popup menu reorganized.
  31. * - Added Chat to Contact command to various popup menus.
  32. * - Added Add to Phonebook to various popup menus.
  33. * - Added hit Enter in SIM editor to edit contact.
  34. * - Added Remove favorite item confirmation message.
  35. * - Added Download entire SIM phonebook.
  36. * - Added New SIM editor columns (phone type, status).
  37. * - Broken Phonebook editors Sorting is not remembered.
  38. *
  39. * Revision 1.17  2004/06/24 09:10:13  z_stoichev
  40. * - Added Chat to Contact command to various popup menus.
  41. * - Added Add to Phonebook to various popup menus.
  42. * - Added hit Enter in SIM editor to edit contact.
  43. * - Added Download entire SIM phonebook.
  44. *
  45. * Revision 1.16  2004/06/23 13:48:53  z_stoichev
  46. * Added Chat support
  47. * Popup menu new items
  48. *
  49. * Revision 1.15  2004/06/22 14:33:17  z_stoichev
  50. * - Fixed SIM database old format detection.
  51. * - Fixed Copy SIM contacts to Phonebook.
  52. * - Fixed Export file type filter misusage/order.
  53. * - Added Import contacts status line feedback.
  54. * - Added Import/Export SIM contacts (vCard only).
  55. * - Added Copy from Phonebook to SIM w/phone type.
  56. *
  57. * Revision 1.14  2004/06/15 13:01:35  z_stoichev
  58. * Fix upload to phone changes detection.
  59. *
  60. * Revision 1.13  2004/05/21 10:09:05  z_stoichev
  61. * Changed logging handle routines.
  62. *
  63. * Revision 1.12  2004/05/19 18:34:16  z_stoichev
  64. * Build 0.1.0.35c
  65. *
  66. * Revision 1.11  2004/03/26 18:37:40  z_stoichev
  67. * Build 0.1.0.35 RC5
  68. *
  69. * Revision 1.10  2004/03/07 21:55:45  z_stoichev
  70. * Apply length restrictions when copy ME to/from SM.
  71. * Sort all in SIM changes misplaced contacts only.
  72. *
  73. * Revision 1.9  2004/01/30 16:42:16  z_stoichev
  74. * Fixed Send contacts from SIM to Phone AV error.
  75. * Added Progress dialog on Uploading contacts.
  76. * Added Fma windows update on SMS Delete/Upload.
  77. *
  78. * Revision 1.8  2004/01/28 17:35:36  z_stoichev
  79. * Popup menu rearranged.
  80. * Allow editing Phone memory.
  81. *
  82. * Revision 1.7  2003/12/12 16:54:24  z_stoichev
  83. * Added view customization support.
  84. *
  85. * Revision 1.6  2003/11/28 09:38:07  z_stoichev
  86. * Merged with branch-release-1-1 (Fma 0.10.28c)
  87. *
  88. * Revision 1.5.2.7  2003/11/26 12:27:18  z_stoichev
  89. * Sort after new contact.
  90. * Select All works now.
  91. *
  92. * Revision 1.5.2.6  2003/11/13 16:37:10  z_stoichev
  93. * Changed images.
  94. *
  95. * Revision 1.5.2.5  2003/11/12 16:21:07  z_stoichev
  96. * Allow contact properties from Explorer popup menu.
  97. *
  98. * Revision 1.5.2.4  2003/11/11 13:11:32  z_stoichev
  99. * Fixed surname is not cleared on edit.
  100. *
  101. * Revision 1.5.2.3  2003/11/10 14:03:10  z_stoichev
  102. * RC3
  103. *
  104. * Revision 1.5.2.2  2003/11/07 13:56:04  z_stoichev
  105. * SIM view/editor recoded.
  106. *
  107. * Revision 1.5.2.1  2003/10/27 07:22:54  z_stoichev
  108. * Build 0.1.0 RC1 Initial Checkin.
  109. *
  110. * Revision 1.5  2003/10/23 11:52:18  z_stoichev
  111. * Fixed bug for max name and tel length.
  112. * Popup menu recreated.
  113. * Font changed.
  114. *
  115. * Revision 1.4  2003/10/21 11:59:31  z_stoichev
  116. * Update SIM sanity checks added.
  117. * Retrieve max name and tel length.
  118. * Bugfixes.
  119. *
  120. * Revision 1.3  2003/01/30 04:15:57  warren00
  121. * Updated with header comments
  122. *
  123. *
  124. *******************************************************************************
  125. }
  126.  
  127. interface
  128.  
  129. uses
  130.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  131.   Grids, ExtCtrls, StdCtrls, Math, Menus, VirtualTrees, ImgList, uSyncPhonebook,
  132.   Placemnt;
  133.  
  134. type
  135.   TSIMData = Record
  136.     imageindex: integer; {0=new,1=modified,2=deleted,3=normal}
  137.     position: integer;
  138.     cname,pnumb: WideString;
  139.     ptype: string[2];
  140.   end;
  141.   PSIMData = ^TSIMData;
  142.  
  143.   TfrmSIMEdit = class(TFrame)
  144.     Panel1: TPanel;
  145.     btnUpdateSIM: TButton;
  146.     cbForce: TCheckBox;
  147.     btnReset: TButton;
  148.     PopupMenu1: TPopupMenu;
  149.     Clear1: TMenuItem;
  150.     Resetchangeflag1: TMenuItem;
  151.     ListNumbers: TVirtualStringTree;
  152.     NoItemsPanel: TPanel;
  153.     N1: TMenuItem;
  154.     N2: TMenuItem;
  155.     Properties1: TMenuItem;
  156.     NewPerson1: TMenuItem;
  157.     UpdateChanged1: TMenuItem;
  158.     UpdateAllRecords1: TMenuItem;
  159.     FormStorage1: TFormStorage;
  160.     N5: TMenuItem;
  161.     UpdateContactsPosition1: TMenuItem;
  162.     UpdateContactsfromPhonebook1: TMenuItem;
  163.     SendAllContactstoPhonebook1: TMenuItem;
  164.     N4: TMenuItem;
  165.     ImportContacts1: TMenuItem;
  166.     ExportContacts1: TMenuItem;
  167.     N6: TMenuItem;
  168.     OpenDialog1: TOpenDialog;
  169.     N3: TMenuItem;
  170.     MessageContact1: TMenuItem;
  171.     CallContact1: TMenuItem;
  172.     ChatContact1: TMenuItem;
  173.     N7: TMenuItem;
  174.     DownloadEntirePhonebook1: TMenuItem;
  175.     SendToPhone1: TMenuItem;
  176.     procedure Button1Click(Sender: TObject);
  177.     procedure StringGridGetEditText(Sender: TObject; ACol, ARow: Integer;
  178.       var Value: String);
  179.     procedure Resetchangeflag1Click(Sender: TObject);
  180.     procedure ListNumbersAfterPaint(Sender: TBaseVirtualTree;
  181.       TargetCanvas: TCanvas);
  182.     procedure ListNumbersCompareNodes(Sender: TBaseVirtualTree; Node1,
  183.       Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
  184.     procedure ListNumbersGetImageIndex(Sender: TBaseVirtualTree;
  185.       Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  186.       var Ghosted: Boolean; var ImageIndex: Integer);
  187.     procedure ListNumbersGetText(Sender: TBaseVirtualTree;
  188.       Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  189.       var CellText: WideString);
  190.     procedure ListNumbersHeaderClick(Sender: TVTHeader;
  191.       Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
  192.       Y: Integer);
  193.     procedure btnUpdateSIMClick(Sender: TObject);
  194.     procedure btnResetClick(Sender: TObject);
  195.     procedure btnDELClick(Sender: TObject);
  196.     procedure btnEDITClick(Sender: TObject);
  197.     procedure PopupMenu1Popup(Sender: TObject);
  198.     procedure NewPerson1Click(Sender: TObject);
  199.     procedure UpdateChanged1Click(Sender: TObject);
  200.     procedure UpdateAllRecords1Click(Sender: TObject);
  201.     procedure UpdateContactsPosition1Click(Sender: TObject);
  202.     procedure UpdateContactsfromPhonebook1Click(Sender: TObject);
  203.     procedure SendAllContactstoPhonebook1Click(Sender: TObject);
  204.     procedure ListNumbersIncrementalSearch(Sender: TBaseVirtualTree;
  205.       Node: PVirtualNode; const SearchText: WideString;
  206.       var Result: Integer);
  207.     procedure ImportContacts1Click(Sender: TObject);
  208.     procedure DownloadEntirePhonebook1Click(Sender: TObject);
  209.     procedure ListNumbersKeyDown(Sender: TObject; var Key: Word;
  210.       Shift: TShiftState);
  211.     procedure FormStorage1SavePlacement(Sender: TObject);
  212.     procedure FormStorage1RestorePlacement(Sender: TObject);
  213.     procedure ListNumbersHeaderMouseUp(Sender: TVTHeader;
  214.       Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  215.   private
  216.     { Private declarations }
  217.     FMaxNumbers: cardinal;
  218.     FMaxNameLen,FMaxTelLen: integer;
  219.     FOldText: String;
  220.     function GetSIMCapacity: Integer;
  221.     function FindFreePos: integer;
  222.     procedure FullRefresh;
  223.     procedure UpdateSIM(MaxItems: cardinal = 0);
  224.     procedure AddLog(Msg: string);
  225.   public
  226.     { Public declarations }
  227.     FContact: TContactData;
  228.     SelContact: PSIMData;
  229.     FRendered: Boolean;
  230.     constructor Create(AOwner: TComponent); override;
  231.     procedure RenderData(ForceSMMode: boolean = False);
  232.     procedure UpdatePhonebook;
  233.     procedure OnConnected;
  234.     procedure ExportList(FileType:Integer; Filename: WideString);
  235.     function IsMEMode: boolean;
  236.     function IsRendered: boolean;
  237.     function FindContact(FullName: WideString; var AContact: PSIMData): boolean; overload;
  238.     function FindContact(FullName,NumberType: WideString; var AContact: PSIMData): boolean; overload;
  239.     function FindContact(FullName: WideString; var ANode: PVirtualNode): boolean; overload;
  240.     function DoEdit(AsNew: boolean = False): boolean;
  241.     procedure CheckForChanges;
  242.   end;
  243.  
  244. implementation
  245.  
  246. uses Unit1, gsm_sms, uGlobal, uEditContact, uConnProgress, uVcard, uXML, WebUtil;
  247.  
  248. {$R *.dfm}
  249.  
  250. { TFrame2 }
  251.  
  252. constructor TfrmSIMEdit.Create(AOwner: TComponent);
  253. begin
  254.   inherited;
  255.   { Set defaults }
  256.   FMaxNameLen := 14; FMaxTelLen := 80; FMaxNumbers := 200;
  257.   FRendered := False;
  258. end;
  259.  
  260. procedure TfrmSIMEdit.RenderData(ForceSMMode: boolean);
  261. var
  262.   sl: TStrings;
  263.   i: Integer;
  264.   Item: PSIMData;
  265.   Node: PVirtualNode;
  266.   s, Name, Kind: WideString;
  267.   Number: String;
  268.   pos,state: Integer;
  269. begin
  270.   { ForceSMMode is used when loading Profile database }
  271.   if not ForceSMMode and IsMEMode then sl := form1.FNodeContactsME.Data
  272.     else sl := form1.FNodeContactsSM.Data;
  273.   try
  274.     ListNumbers.BeginUpdate;
  275.     ListNumbers.Clear;
  276.     ListNumbers.NodeDataSize := sizeof(TSIMData);
  277.     try
  278.       try
  279.         for i := 0 to sl.Count - 1 do begin
  280.           s := sl[i];
  281.           {
  282.           Name := GetToken(s, 0);
  283.           Number := GetToken(s, 1);
  284.           Pos := StrToInt(GetToken(s, 2));
  285.  
  286.           try
  287.             state := StrToInt(GetToken(s, 3));
  288.           except
  289.             state := 3;
  290.           end;
  291.           }
  292.           Name := GetFirstToken(s);
  293.           Number := GetFirstToken(s);
  294.           Pos := StrToInt(GetFirstToken(s));
  295.           state := StrToInt(GetFirstToken(s));
  296.  
  297.           Node := ListNumbers.AddChild(nil);
  298.           item := ListNumbers.GetNodeData(Node);
  299.  
  300.           Form1.ExtractName(Name,Kind);
  301.  
  302.           with item^ do begin
  303.             cname := Name;
  304.             pnumb := Number;
  305.             ptype := Kind;
  306.             imageindex := state;
  307.             position := Pos;
  308.           end;
  309.         end;
  310.  
  311.         FRendered := True;
  312.       except
  313.         ShowMessage('Old data format, please refresh the folder.');
  314.       end;
  315.     finally
  316.       ListNumbers.EndUpdate;
  317.       ListNumbers.Sort(nil, ListNumbers.Header.SortColumn, ListNumbers.Header.SortDirection);
  318.     end;
  319.   except
  320.   end;  
  321. end;
  322.  
  323. procedure TfrmSIMEdit.Button1Click(Sender: TObject);
  324. begin
  325.   if not Form1.FConnected then Form1.ActionConnectionConnectExecute(Self);
  326.  
  327.   Form1.ActionContactsDownloadExecute(Self);
  328.   RenderData;
  329. end;
  330.  
  331. procedure TfrmSIMEdit.UpdateSIM(MaxItems: cardinal);
  332. var
  333.   PerformCleanup: boolean;
  334.   i,maxPos,LastPos,numType: Integer;
  335.   w, name, number, buf: WideString;
  336.   Item: PSIMData;
  337.   Node: PVirtualNode;
  338.   frmConnect: TfrmConnect;
  339.   target: string;
  340. begin
  341.   if IsMEMode then target := 'Phonebook' else target := 'SIM';
  342.   PerformCleanup := MaxItems = ListNumbers.ChildCount[nil];
  343.   Form1.RequestConnection;
  344.   AddLog('Update '+target+' started.');
  345.   frmConnect := GetProgressDialog;
  346.   try
  347.     if Form1.CanShowProgress then
  348.       frmConnect.ShowProgress(Form1.FProgressLongOnly);
  349.     if MaxItems > 0 then
  350.       frmConnect.Initialize(MaxItems,'Updating '+target)
  351.     else
  352.       frmConnect.SetDescr('Updating '+target);
  353.     Form1.Status('Uploading contacts...');
  354.     maxPos := 0;
  355.     try
  356.       if IsMEMode then Form1.TxAndWait('AT+CPBS="ME"')
  357.         else Form1.TxAndWait('AT+CPBS="SM"');
  358.       try
  359.         Node := ListNumbers.GetFirst;
  360.         while Node <> nil do
  361.         try
  362.           item := ListNumbers.GetNodeData(Node);
  363.           if (item.imageindex <> 3) or cbForce.Checked then begin
  364.             numType := 129;
  365.             Name := item.cname;
  366.             Number := item.pnumb;
  367.             if Form1.FUseUTF8 then Name := UTF8Encode(Name);
  368.  
  369.             if maxPos <= item.position then maxPos := item.position;
  370.  
  371.             buf := 'AT+CPBW=' + IntToStr(item.position);
  372.             if Item.imageindex = 2 then begin
  373.               // Delete
  374.               AddLog(Name + ' deleted in '+target+' by FMA.');
  375.               Form1.TxAndWait(buf);
  376.             end
  377.             else begin
  378.               // Update
  379.               if Number[1] = '+' then begin
  380.                 Number := copy(Number, 2, length(Number));
  381.                 numType := 145;
  382.               end;
  383.               w := Name;
  384.               { NOTE: When writing to SM, <text> shall be written as ôlast nameö + comma +
  385.                 white space +öfirst nameö + ô/ö + <type_of_number> }
  386.               if IsMEMode then begin
  387.                 { This is not working as described in the docs :/
  388.                 i := Pos(' ',w);
  389.                 if i <> 0 then
  390.                   w := Copy(w,i+1,Length(w)) + ', ' + Copy(w,1,i-1);
  391.                 }
  392.                 if (Length(w) < (FMaxNameLen-1)) and (item.ptype <> '') then
  393.                 case item.ptype[1] of
  394.                   'M': w := w + '/M';
  395.                   'H': w := w + '/H';
  396.                   'W': w := w + '/W';
  397.                   'F': w := w + '/F';
  398.                   'O': w := w + '/O';
  399.                 end;
  400.               end
  401.               else begin
  402.                 { HACK! remove obsolete '/O' from SIM entries, added by previous FMA releases HACK! }
  403.                 i := Length(w);
  404.                 if (i > 2) and (w[i-1] = '/') and (Char(w[i]) in ['M','H','W','F','O']) then
  405.                   Delete(w,i-1,2);
  406.               end;
  407.               buf := buf + ',"' + Number + '",' + IntToStr(numType) + ',"' + w + '"';
  408.               Form1.TxAndWait(buf);
  409.               AddLog(Name + ' stored in '+target+' by FMA.');
  410.               Item.imageindex := 3;
  411.             end;
  412.           end;
  413.         finally
  414.           frmConnect.IncProgress(1);
  415.           Node := ListNumbers.GetNext(Node);
  416.           ListNumbers.Repaint;
  417.         end;
  418.         { Ususaly happens when you rearrange contacts positions, i.e. modify all of them }
  419.         if PerformCleanup then begin
  420.           Form1.Status('Performing cleanup...');
  421.           frmConnect.SetDescr('Performing cleanup');
  422.           frmConnect.SEProgress1.UnknownMax := True;
  423.           if IsMEMode then LastPos := Form1.frmSyncPhonebook.FMaxRecME
  424.             else LastPos := FMaxNumbers;
  425.           for maxPos := maxPos + 1 to LastPos do begin
  426.             buf := 'AT+CPBW=' + IntToStr(maxPos);
  427.             Form1.TxAndWait(buf);
  428.           end;
  429.         end;
  430.       finally
  431.         UpdatePhonebook;
  432.       end;
  433.     except
  434.       Form1.Status('Error updating memory');
  435.       ShowMessage('Error updating memory');
  436.       AddLog('Update '+target+' failed.');
  437.     end;
  438.     AddLog('Update '+target+' completed.');
  439.   finally
  440.     FreeProgressDialog;
  441.     Form1.Status('');
  442.   end;  
  443. end;
  444.  
  445. procedure TfrmSIMEdit.btnUpdateSIMClick(Sender: TObject);
  446. var
  447.   Item: PSIMData;
  448.   Node: PVirtualNode;
  449.   mcount,dcount,count: cardinal;
  450.   s: string;
  451.   err: boolean;
  452. begin
  453.   mcount := 0; dcount := 0;
  454.  
  455.   Node := ListNumbers.GetFirst;
  456.   while Node <> nil do
  457.   try
  458.     item := ListNumbers.GetNodeData(Node);
  459.     if item.imageindex = 2 then
  460.       // deleted contact
  461.       inc(dcount)
  462.     else
  463.       // then process modified (or all) entries
  464.       if (item.imageindex <> 3) or cbForce.Checked then begin
  465.         { Do sanity check for modified contacts }
  466.         if (Trim(item.cname) = '') and (Trim(item.pnumb) <> '') then
  467.           raise EConvertError.Create('Please enter a contact name at position '+IntToStr(item.position));
  468.         if (Trim(item.cname) <> '') and (Trim(item.pnumb) = '') then
  469.           raise EConvertError.Create('Please enter a phone number at position '+IntToStr(item.position));
  470.         if IsMEMode then err := cardinal(Length(item.cname)) > Form1.frmSyncPhonebook.FMaxNameLen
  471.           else err := Length(item.cname) > FMaxNameLen;
  472.         if err then raise EConvertError.Create('The name at position '+IntToStr(item.position)+' is too long');
  473.         if IsMEMode then err := cardinal(Length(item.pnumb)) > (Form1.frmSyncPhonebook.FMaxTelLen+byte(Pos('+',item.pnumb) <> 0))
  474.           else err := Length(item.pnumb) > (FMaxTelLen+byte(Pos('+',item.pnumb) <> 0));
  475.         if err then raise EConvertError.Create('The number at position '+IntToStr(item.position)+' is too long');
  476.         inc(mcount);
  477.       end;
  478.   finally
  479.     Node := ListNumbers.GetNext(Node);
  480.   end;
  481.  
  482.   count := mcount + dcount;
  483.   if count <> 0 then begin
  484.     if mcount <> 0 then begin
  485.       s := IntToStr(mcount) + ' modified contact';
  486.       if mcount <> 1 then s := s + 's';
  487.     end
  488.     else
  489.       s := '';
  490.     if dcount <> 0 then begin
  491.       if mcount <> 0 then s := s + ' and ';
  492.       s := s + IntToStr(dcount) + ' deleted contact';
  493.       if dcount <> 1 then s := s + 's';
  494.     end;
  495.     case MessageDlg('Changes have been made into current folder.'#13#13+
  496.       'Confirm sending ' + s + ' to phone?', mtConfirmation, [mbYes, mbNO, mbCANCEL], 0) of
  497.       mrYes: UpdateSIM(count);
  498.       mrCancel: Abort;
  499.     end;
  500.   end;
  501. end;
  502.  
  503. procedure TfrmSIMEdit.btnResetClick(Sender: TObject);
  504. var
  505.   Item: PSIMData;
  506.   Node: PVirtualNode;
  507. begin
  508.   ListNumbers.BeginUpdate;
  509.   Node := ListNumbers.GetFirst;
  510.   while Node <> nil do
  511.   try
  512.     item := ListNumbers.GetNodeData(Node);
  513.     item.imageindex := 3; // mark as not modified
  514.   finally
  515.     Node := ListNumbers.GetNext(Node);
  516.   end;
  517.   ListNumbers.EndUpdate;
  518.   UpdatePhonebook;
  519. end;
  520.  
  521. procedure TfrmSIMEdit.StringGridGetEditText(Sender: TObject; ACol,
  522.   ARow: Integer; var Value: String);
  523. begin
  524.   FOldText := Value;
  525. end;
  526.  
  527. procedure TfrmSIMEdit.btnDELClick(Sender: TObject);
  528. var
  529.   Item: PSIMData;
  530.   Node,Tmp: PVirtualNode;
  531.   s: string;
  532. begin
  533.   if ListNumbers.SelectedCount = 0 then exit;
  534.   s := 'Deleting ' + IntToStr(ListNumbers.SelectedCount) + ' item(s)';
  535.   if MessageDlg(s+'. Do you wish to continue?',mtConfirmation,[mbYes,mbNo],0) <> ID_YES then
  536.     exit;
  537.   Form1.Status(s+'...');
  538.   ListNumbers.BeginUpdate;
  539.   try
  540.     Node := ListNumbers.GetFirst;
  541.     while Node <> nil do begin
  542.       if ListNumbers.Selected[Node] then begin
  543.         item := ListNumbers.GetNodeData(Node);
  544.         if item.imageindex <> 0 then begin
  545.           item.imageindex := 2;
  546.         end
  547.         else begin
  548.           Tmp := Node;
  549.           if Node <> ListNumbers.GetFirst then begin
  550.             Node := ListNumbers.GetPrevious(Node);
  551.             ListNumbers.DeleteNode(Tmp);
  552.           end
  553.           else begin
  554.             ListNumbers.DeleteNode(Tmp);
  555.             Node := ListNumbers.GetFirst;
  556.             continue;
  557.           end;
  558.         end;
  559.       end;
  560.       Node := ListNumbers.GetNext(Node);
  561.     end;
  562.   finally
  563.     ListNumbers.EndUpdate;
  564.     UpdatePhonebook;
  565.   end;  
  566.   Form1.Status('');
  567. end;
  568.  
  569. procedure TfrmSIMEdit.Resetchangeflag1Click(Sender: TObject);
  570. var
  571.   Item: PSIMData;
  572.   Node: PVirtualNode;
  573. begin
  574.   ListNumbers.BeginUpdate;
  575.   Node := ListNumbers.GetFirst;
  576.   while Node <> nil do
  577.   try
  578.     if ListNumbers.Selected[Node] then begin
  579.       item := ListNumbers.GetNodeData(Node);
  580.       item.imageindex := 3;
  581.     end;
  582.   finally
  583.     Node := ListNumbers.GetNext(Node);
  584.   end;
  585.   ListNumbers.EndUpdate;
  586.   UpdatePhonebook;
  587. end;
  588.  
  589. function TfrmSIMEdit.GetSIMCapacity: Integer;
  590. var
  591.   i: Integer;
  592.   buffer, stop: String;
  593.   slTmp: TStrings;
  594. begin
  595.   Form1.TxAndWait('AT+CPBS="SM"');
  596.   Form1.TxAndWait('AT+CPBR=?');
  597.   // defaults
  598.   buffer := '';
  599.   stop := '200'; FMaxNameLen := 14; FMaxTelLen := 80;
  600.   // +CPBR: (1-200),80,14
  601.   for i := 0 to Form1.FRxBuffer.Count-1 do
  602.     if Pos('+CPBR',Form1.FRxBuffer.Strings[i]) = 1 then begin
  603.       buffer := Form1.FRxBuffer.Strings[i];
  604.       break;
  605.     end;
  606.   for i := 1 to length(buffer) do begin
  607.     if IsDelimiter('()-,', buffer, i) then buffer[i] := ' ';
  608.   end;
  609.   // +CPBR:  1 200  80 14
  610.   if buffer <> '' then begin
  611.     slTmp := TStringList.Create;
  612.     try
  613.       slTmp.DelimitedText := buffer;
  614.       stop := slTmp.Strings[2];
  615.       Form1.Debug('SIM: max entries = '+stop);
  616.       FMaxTelLen := StrToInt(slTmp.Strings[3]);
  617.       Form1.Debug('SIM: max tel length = '+slTmp.Strings[3]);
  618.       FMaxNameLen := StrToInt(slTmp.Strings[4]);
  619.       Form1.Debug('SIM: max name length = '+slTmp.Strings[4]);
  620.     finally
  621.       slTmp.Free;
  622.     end;
  623.   end;  
  624.   Result := StrToInt(stop);
  625. end;
  626.  
  627. procedure TfrmSIMEdit.OnConnected;
  628. begin
  629.   FMaxNumbers := GetSIMCapacity;
  630. end;
  631.  
  632. procedure TfrmSIMEdit.ListNumbersAfterPaint(Sender: TBaseVirtualTree;
  633.   TargetCanvas: TCanvas);
  634. begin
  635.   NoItemsPanel.Visible := ListNumbers.ChildCount[nil] = 0;
  636. end;
  637.  
  638. procedure TfrmSIMEdit.ListNumbersCompareNodes(Sender: TBaseVirtualTree;
  639.   Node1, Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
  640. var
  641.   SIM1, SIM2: PSIMData;
  642. begin
  643.   SIM1 := Sender.GetNodeData(Node1);
  644.   SIM2 := Sender.GetNodeData(Node2);
  645.  
  646.   if Column = 0 then begin
  647.     if SIM1.position > SIM2.position then
  648.       Result := 1
  649.     else
  650.       if SIM1.position < SIM2.position then
  651.         Result := -1
  652.       else
  653.         Result := 0;  
  654.   end
  655.   else if Column = 1 then Result := WideCompareStr(SIM1.cname, SIM2.cname)
  656.   else if Column = 2 then Result := WideCompareStr(SIM1.pnumb, SIM2.pnumb);
  657. end;
  658.  
  659. procedure TfrmSIMEdit.ListNumbersGetImageIndex(Sender: TBaseVirtualTree;
  660.   Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  661.   var Ghosted: Boolean; var ImageIndex: Integer);
  662. var
  663.   SIM: PSIMData;
  664. begin
  665.   if Column = 0 then begin
  666.     if (Kind = ikNormal) or (Kind = ikSelected) then begin
  667.       SIM := Sender.GetNodeData(Node);
  668.       ImageIndex := SIM.imageindex;
  669.     end
  670.     else ImageIndex := -1;
  671.   end;
  672. end;
  673.  
  674. procedure TfrmSIMEdit.ListNumbersGetText(Sender: TBaseVirtualTree;
  675.   Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  676.   var CellText: WideString);
  677. var
  678.   SIM: PSIMData;
  679. begin
  680.   SIM := Sender.GetNodeData(Node);
  681.  
  682.   if Column = 0 then CellText := IntToStr(SIM.position)
  683.   else if Column = 1 then CellText := SIM.cname
  684.   else if Column = 2 then CellText := SIM.pnumb
  685.   else if Column = 3 then begin
  686.     CellText := '';
  687.     if SIM.ptype = 'M' then CellText := 'Cell';
  688.     if SIM.ptype = 'W' then CellText := 'Work';
  689.     if SIM.ptype = 'H' then CellText := 'Home';
  690.     if SIM.ptype = 'O' then CellText := 'Other';
  691.     if SIM.ptype = 'F' then CellText := 'Fax';
  692.   end
  693.   else if Column = 4 then begin
  694.     CellText := '';
  695.     case SIM.imageindex of
  696.       0: CellText := 'New contact';
  697.       1: CellText := 'Modified contact';
  698.       2: CellText := 'Deleted contact';
  699.       3: CellText := '';
  700.     end;
  701.   end;
  702. end;
  703.  
  704. procedure TfrmSIMEdit.ListNumbersHeaderClick(Sender: TVTHeader;
  705.   Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
  706.   Y: Integer);
  707. begin
  708.   if Button = mbLeft then begin
  709.     if Column = Sender.SortColumn then begin
  710.       if Sender.SortDirection = sdDescending then
  711.         Sender.SortDirection := sdAscending
  712.       else
  713.         Sender.SortDirection := sdDescending;
  714.     end
  715.     else
  716.       Sender.SortColumn := Column;
  717.     ListNumbers.Sort(nil, ListNumbers.Header.SortColumn, ListNumbers.Header.SortDirection);
  718.   end;
  719. end;
  720.  
  721. procedure TfrmSIMEdit.btnEDITClick(Sender: TObject);
  722. var
  723.   Node :PVirtualNode;
  724. begin
  725.   Node := ListNumbers.FocusedNode;
  726.   if Node <> nil then begin
  727.     SelContact := ListNumbers.GetNodeData(Node);
  728.     DoEdit;
  729.   end;
  730. end;
  731.  
  732. procedure TfrmSIMEdit.PopupMenu1Popup(Sender: TObject);
  733. begin
  734.   Properties1.Enabled := ListNumbers.SelectedCount = 1;
  735.   SendAllContactstoPhonebook1.Enabled := not Form1.FStartupOptions.NoIRMC;
  736.   SendAllContactstoPhonebook1.Visible := not IsMEMode;
  737.   UpdateContactsfromPhonebook1.Enabled := SendAllContactstoPhonebook1.Enabled;
  738.   UpdateContactsfromPhonebook1.Visible := SendAllContactstoPhonebook1.Visible;
  739.   N3.Visible := SendAllContactstoPhonebook1.Visible;
  740. end;
  741.  
  742. function TfrmSIMEdit.DoEdit(AsNew: boolean): boolean;
  743. var
  744.   Node: PVirtualNode;
  745.   i: integer;
  746. begin
  747.   Result := False;
  748.   with TfrmEditContact.Create(nil) do
  749.   try
  750.     // set restrictions
  751.     MaxFullNameLen :=  FMaxNameLen;
  752.     txtName.MaxLength := FMaxNameLen;
  753.     txtCell.MaxLength := FMaxTellen;
  754.     // update contact state
  755.     if AsNew then FillChar(FContact,SizeOf(FContact),0)
  756.     else begin
  757.       { Set contact name and surname }
  758.       i := Pos(' ',Selcontact^.cname);
  759.       if i <> 0 then begin
  760.         FContact.name := Copy(Selcontact^.cname,1,i-1);
  761.         FContact.surname := Copy(Selcontact^.cname,i+1,Length(Selcontact^.cname)-i);
  762.       end
  763.       else begin
  764.         FContact.name := Selcontact^.cname;
  765.         FContact.surname := '';
  766.       end;
  767.       { Set number according to phone type }
  768.       FContact.cell := '';
  769.       FContact.work := '';
  770.       FContact.home := '';
  771.       FContact.fax := '';
  772.       FContact.other := '';
  773.       if Selcontact^.pnumb <> '' then
  774.         if not IsMEMode or (Selcontact^.ptype = '') then
  775.           FContact.cell := Selcontact^.pnumb
  776.         else
  777.           case Selcontact^.ptype[1] of
  778.             'M': FContact.cell := Selcontact^.pnumb;
  779.             'W': FContact.work := Selcontact^.pnumb;
  780.             'H': FContact.home := Selcontact^.pnumb;
  781.             'F': FContact.fax := Selcontact^.pnumb;
  782.             'O': FContact.other := Selcontact^.pnumb;
  783.           end;
  784.     end;
  785.     contact := FContact;
  786.     UseSIMMode := True;  
  787.     // edit contact as a SIM entry  
  788.     if ShowModal = mrOk then begin
  789.       if Modified then with ListNumbers do begin
  790.         // apply total updates
  791.         BeginUpdate;
  792.         try
  793.           if AsNew then begin // create new node
  794.             Node := AddChild(nil);
  795.             Selcontact := ListNumbers.GetNodeData(Node);
  796.           end
  797.           else
  798.             Node := nil;  
  799.           try
  800.             { copy all data }
  801.             FContact := contact;
  802.             Selcontact^.cname := FContact.name;
  803.             if FContact.surname <> '' then
  804.               Selcontact^.cname := Selcontact^.cname + ' ' + FContact.surname;
  805.  
  806.             { get new number }
  807.             if AsNew then begin
  808.               if FContact.cell <> '' then begin
  809.                 Selcontact^.pnumb := FContact.cell;
  810.                 Selcontact^.ptype := 'M';
  811.               end;
  812.               if FContact.work <> '' then begin
  813.                 Selcontact^.pnumb := FContact.work;
  814.                 Selcontact^.ptype := 'W';
  815.               end;
  816.               if FContact.home <> '' then begin
  817.                 Selcontact^.pnumb := FContact.home;
  818.                 Selcontact^.ptype := 'H';
  819.               end;
  820.               if FContact.fax <> '' then begin
  821.                 Selcontact^.pnumb := FContact.fax;
  822.                 Selcontact^.ptype := 'F';
  823.               end;
  824.               if FContact.other <> '' then begin
  825.                 Selcontact^.pnumb := FContact.other;
  826.                 Selcontact^.ptype := 'O';
  827.               end;
  828.             end
  829.             else
  830.               if not IsMEMode or (Selcontact^.ptype = '') then
  831.                 Selcontact^.pnumb := FContact.cell
  832.               else
  833.                 case Selcontact^.ptype[1] of
  834.                   'M': Selcontact^.pnumb := FContact.cell;
  835.                   'W': Selcontact^.pnumb := FContact.work;
  836.                   'H': Selcontact^.pnumb := FContact.home;
  837.                   'F': Selcontact^.pnumb := FContact.fax;
  838.                   'O': Selcontact^.pnumb := FContact.other;
  839.                 end;
  840.  
  841.             if AsNew then begin
  842.               Selcontact^.imageindex := 0;
  843.               Selcontact^.position := FindFreePos;
  844.               if Selcontact^.position = -1 then
  845.                 DeleteNode(Node)
  846.               else
  847.                 Result := True;
  848.             end
  849.             else
  850.               if Selcontact^.imageindex <> 0 then begin // mark as modified
  851.                 Selcontact^.imageindex := 1;
  852.                 Result := True;
  853.               end;
  854.           except;
  855.             if AsNew then DeleteNode(Node);
  856.             raise;
  857.           end;
  858.           UpdatePhonebook;
  859.         finally
  860.           EndUpdate;
  861.         end;
  862.       end; 
  863.     end;
  864.   finally
  865.     Free;
  866.   end;
  867.   ListNumbers.Sort(nil, ListNumbers.Header.SortColumn, ListNumbers.Header.SortDirection);
  868. end;
  869.  
  870. procedure TfrmSIMEdit.NewPerson1Click(Sender: TObject);
  871. var
  872.   OK: boolean;
  873. begin
  874.   if IsMEMode then
  875.     OK := ListNumbers.RootNodeCount < Form1.frmSyncPhonebook.FMaxRecME
  876.   else
  877.     OK := ListNumbers.RootNodeCount < FMaxNumbers;
  878.   if OK then
  879.     DoEdit(True)
  880.   else
  881.     ShowMessage('No more space in memory! New contact can not be created.');
  882. end;
  883.  
  884. procedure TfrmSIMEdit.UpdateChanged1Click(Sender: TObject);
  885. begin
  886.   cbForce.Checked := False;
  887.   btnUpdateSIM.Click;
  888. end;
  889.  
  890. procedure TfrmSIMEdit.UpdateAllRecords1Click(Sender: TObject);
  891. begin
  892.   cbForce.Checked := True;
  893.   btnUpdateSIM.Click;
  894. end;
  895.  
  896. procedure TfrmSIMEdit.AddLog(Msg: string);
  897. begin
  898.   Form1.SyncLog(Msg);
  899. end;
  900.  
  901. function TfrmSIMEdit.FindFreePos: integer;
  902. var
  903.   Node :PVirtualNode;
  904.   Item: PSIMData;
  905.   Pos: cardinal;
  906.   found: boolean;
  907. begin
  908.   Pos := 1;
  909.   while Pos <= FMaxNumbers do begin
  910.     found := false;
  911.     Node := ListNumbers.GetFirst;
  912.     while Node <> nil do
  913.     try
  914.       item := ListNumbers.GetNodeData(Node);
  915.       if cardinal(item.position) = Pos then begin
  916.         found := true;
  917.         break;
  918.       end;
  919.     finally
  920.       Node := ListNumbers.GetNext(Node);
  921.     end;
  922.     if not found then break;
  923.     Pos := Pos + 1;
  924.   end;
  925.   if Pos <= FMaxNumbers then Result := Pos
  926.     else Result := -1;
  927. end;
  928.  
  929. procedure TfrmSIMEdit.UpdateContactsPosition1Click(Sender: TObject);
  930. var
  931.   Pos: Integer;
  932.   Item: PSIMData;
  933.   Node: PVirtualNode;
  934. begin
  935.   if MessageDlg('Sorting contacts will replace all contacts position. Are you sure?',
  936.     mtConfirmation, [mbYes, mbNO], 0) <> mrYes then exit;
  937.   Pos := 1;
  938.   try
  939.     Node := ListNumbers.GetFirst;
  940.     while Node <> nil do
  941.     try
  942.       item := ListNumbers.GetNodeData(Node);
  943.       if item.position <> Pos then begin
  944.         item.position := Pos;
  945.         item.imageindex := 1; // mark as modified
  946.       end;
  947.       inc(Pos);
  948.     finally
  949.       Node := ListNumbers.GetNext(Node);
  950.     end;
  951.   finally
  952.     ListNumbers.Sort(nil, ListNumbers.Header.SortColumn, ListNumbers.Header.SortDirection);
  953.     UpdatePhonebook;
  954.   end;  
  955. end;
  956.  
  957. procedure TfrmSIMEdit.UpdateContactsfromPhonebook1Click(Sender: TObject);
  958. var
  959.   sl,dl: TStrings;
  960.   i: integer;
  961.   s,kind,item,t: WideString;
  962. begin
  963.   sl := TStrings(Form1.FNodeContactsME.Data); // source
  964.   dl := TStrings(Form1.FNodeContactsSM.Data); // destination
  965.  
  966.   if ListNumbers.ChildCount[nil] <> 0 then begin
  967.     if sl.Count = 0 then begin
  968.       if MessageDlg('Phonebook is empty. All current SIM entries will be deleted. Continue anyway?',
  969.         mtConfirmation, [mbYes, mbNO], 0) <> mrYes then exit;
  970.     end
  971.     else
  972.       if MessageDlg('Copy all contacts from Phonebook. Current SIM entries will be deleted. Are you sure?',
  973.         mtConfirmation, [mbYes, mbNO], 0) <> mrYes then exit;
  974.   end;
  975.  
  976.   { Clear SIM database }
  977.   dl.Clear;
  978.   Form1.FNodeContactsSM.DeleteChildren;
  979.   try
  980.     { Build new database }
  981.     for i := 0 to sl.Count-1 do begin
  982.       // '"Alycia/M",+359887555555555,0'
  983.       { get full name }
  984.       item := GetToken(sl.Strings[i],0);
  985.       Form1.ExtractName(item,kind);
  986.       if kind <> '' then kind := '/' + kind;
  987.       item := Copy(item,1,FMaxNameLen) + kind;
  988.       { get number }
  989.       t := GetToken(sl.Strings[i],1);
  990.       s := Copy(t,1,FMaxTelLen+byte(Pos('+',t) <> 0));
  991.       { Construct a database item }
  992.       item := '"' + item + '",' + s + ',' + IntToStr(i+1) + ',1'; // and mark (1) as modified 
  993.       { Add to SIM database }
  994.       dl.Add(item);
  995.     end;
  996.   finally
  997.     Form1.RenderContactList(Form1.FNodeContactsSM);
  998.     { Update view }
  999.     RenderData;
  1000.     Form1.Status('Copy from Phonebook: ' + IntToStr(ListNumbers.ChildCount[nil]) + ' new items');
  1001.   end;  
  1002. end;
  1003.  
  1004. procedure TfrmSIMEdit.SendAllContactstoPhonebook1Click(Sender: TObject);
  1005. var
  1006.   Item: PSIMData;
  1007.   Node,NewNode: PVirtualNode;
  1008.   Contact: PContactData;
  1009.   NewCnt,UpdCnt: cardinal;
  1010.   s,T,N: WideString;
  1011.   i,j: integer;
  1012. begin
  1013.   NewCnt := 0;
  1014.   UpdCnt := 0;
  1015.   try
  1016.     Node := ListNumbers.GetFirst;
  1017.     while Node <> nil do
  1018.     try
  1019.       item := ListNumbers.GetNodeData(Node);
  1020.       { Sanity check phone number length }
  1021.       i := Form1.frmSyncPhonebook.FMaxTellen;
  1022.       if Length(item.pnumb) < i then i := Length(item.pnumb);
  1023.       s := Copy(item.pnumb,1+Length(item.pnumb)-i,i);
  1024.       { If contact already exists? }
  1025.       if Form1.frmSyncPhonebook.FindContact(item.cname,contact) then begin
  1026.         { Check if the number already exists for that contact
  1027.         O := GetContactPhoneType(contact,s); // get type if number found in ME contact
  1028.         if O = '' then T := item.ptype
  1029.           else T := O;
  1030.         }
  1031.         T := item.ptype;
  1032.         {}
  1033.         if T = 'W' then N := contact^.work else
  1034.         if T = 'H' then N := contact^.home else
  1035.         if T = 'F' then N := contact^.fax else
  1036.         if T = 'O' then N := contact^.other else
  1037.           N := contact^.cell; // Default to '' or 'M' phone type
  1038.         { Number position already filled in? }  
  1039.         if N <> '' then
  1040.           // TODO: Add wizard for asking where to store new number
  1041.           if MessageDlg('(TODO: add wizard here) Contact '+item.cname+' already exists.'#13#13+
  1042.             'Do you want to replace its Phonebook number [' + N + '] with current SIM number [' + s + '] ?',
  1043.             mtConfirmation, [mbYes, mbNO], 0) <> mrYes then
  1044.             continue;
  1045.         if T = 'W' then contact^.work := s else
  1046.         if T = 'H' then contact^.home := s else
  1047.         if T = 'F' then contact^.fax := s else
  1048.         if T = 'O' then contact^.other := s else
  1049.           contact^.cell := s;
  1050.         contact^.StateIndex := 1; // contact modified
  1051.         inc(UpdCnt);
  1052.       end
  1053.       else begin
  1054.         NewNode := Form1.frmSyncPhonebook.ListContacts.AddChild(nil);
  1055.         contact := Form1.frmSyncPhonebook.ListContacts.GetNodeData(NewNode);
  1056.         FillChar(contact^,SizeOf(contact^),0);
  1057.         contact^.cell := item.pnumb;
  1058.         contact^.StateIndex := 0; // new contact
  1059.         s := Copy(item.cname,1,Form1.frmSyncPhonebook.FMaxNameLen+byte(Pos(' ',s) <> 0)); // sanity check name length
  1060.         j := Pos(' ',s);
  1061.         if j = 0 then
  1062.           contact^.name := s
  1063.         else begin
  1064.           contact^.name := Copy(s,1,j-1);
  1065.           contact^.surname := Copy(s,j+1,Length(s)-j);
  1066.         end;
  1067.         inc(NewCnt);
  1068.       end;
  1069.     finally
  1070.       Node := ListNumbers.GetNext(Node);
  1071.     end;
  1072.   finally
  1073.     if (NewCnt + UpdCnt) <> 0 then Form1.UpdateMEPhonebook;
  1074.     Form1.Status('Copy to Phonebook: ' + IntToStr(NewCnt)+' new, ' + IntToStr(UpdCnt) + ' modified and ' +
  1075.       IntToStr(ListNumbers.ChildCount[nil] - NewCnt - UpdCnt) + ' items skipped');
  1076.   end;
  1077. end;
  1078.  
  1079. function TfrmSIMEdit.IsMEMode: boolean;
  1080. begin
  1081.   Result := Form1.Explorer.Selected = Form1.FNodeContactsME;
  1082. end;
  1083.  
  1084. procedure TfrmSIMEdit.UpdatePhonebook;
  1085. begin
  1086.   if IsMEMode then
  1087.     Form1.UpdateMEPhonebook
  1088.   else
  1089.     Form1.UpdateSMPhonebook;
  1090. end;
  1091.  
  1092. function TfrmSIMEdit.IsRendered: boolean;
  1093. const
  1094.   LastIsME: boolean = False;
  1095. begin
  1096.   Result := (LastIsME = IsMEMode) and FRendered;
  1097.   LastIsME := IsMEMode;
  1098. end;
  1099.  
  1100. procedure TfrmSIMEdit.CheckForChanges;
  1101. var
  1102.   Modified: boolean;
  1103.   Contact: PSIMData;
  1104.   Node :PVirtualNode;
  1105. begin
  1106.   Modified := False;
  1107.   Node := ListNumbers.GetFirst;
  1108.   while Node <> nil do
  1109.   try
  1110.     Contact := ListNumbers.GetNodeData(Node);
  1111.     if Contact^.imageindex <> 3 then begin
  1112.       Modified := True;
  1113.       break;
  1114.     end;
  1115.   finally
  1116.     Node := ListNumbers.GetNext(Node);
  1117.   end;
  1118.   if Modified then UpdateChanged1.Click;
  1119. end;
  1120.  
  1121. procedure TfrmSIMEdit.ListNumbersIncrementalSearch(
  1122.   Sender: TBaseVirtualTree; Node: PVirtualNode;
  1123.   const SearchText: WideString; var Result: Integer);
  1124. var
  1125.   SIM: PSIMData;
  1126.   Text: WideString;
  1127. begin
  1128.   SIM := Sender.GetNodeData(Node);
  1129.   Text := Copy(SIM.cname,1,Length(SearchText));
  1130.   Result := WideCompareText(SearchText,Text);
  1131. end;
  1132.  
  1133. procedure TfrmSIMEdit.ImportContacts1Click(Sender: TObject);
  1134. var
  1135.   i,j,adds,mods: integer;
  1136.   Node: PVirtualNode;
  1137.   AContact: TContactData;
  1138.   PContact: PContactData;
  1139.   contact: PSIMData;
  1140.   sl: TStringList;
  1141.   F,N,T: WideString;
  1142.   Modified: boolean;
  1143.   dlg: TfrmConnect;
  1144.   VCard: TVCard;
  1145. begin
  1146.   if not OpenDialog1.Execute then exit;
  1147.   Update;
  1148.   PContact := @AContact;
  1149.   dlg := GetProgressDialog;
  1150.   VCard := TVCard.Create;
  1151.   try
  1152.     if Form1.CanShowProgress then
  1153.       dlg.ShowProgress(Form1.FProgressLongOnly);
  1154.     dlg.Initialize(OpenDialog1.Files.Count,'Importing SIM contacts');
  1155.  
  1156.     Form1.Status('Importing contacts...');
  1157.     //SyncLog('Import started');
  1158.  
  1159.     adds := 0; mods := 0;
  1160.     //ListContacts.BeginUpdate;
  1161.     sl := TStringList.Create;
  1162.     try
  1163.       for i := 0 to OpenDialog1.Files.Count-1 do begin
  1164.         sl.LoadFromFile(OpenDialog1.Files[i]);
  1165.         dlg.IncProgress(1);
  1166.         VCard.Clear;
  1167.         VCard.Raw := sl;
  1168.         vCard2Contact(VCard,PContact);
  1169.         F := GetvCardFullName(VCard);
  1170.         { Process all contact numbers }
  1171.         for j := 1 to 4 do begin
  1172.           case j of
  1173.             1: N := PContact^.cell;
  1174.             2: N := PContact^.work;
  1175.             3: N := PContact^.home;
  1176.             4: N := PContact^.other;
  1177.           end;
  1178.           if N = '' then continue;
  1179.           T := GetContactPhoneType(PContact,N);
  1180.           Modified := False;
  1181.           // ask to replace old record, if present
  1182.           if FindContact(F,T,contact) then begin
  1183.             { TODO: Add dialog with replace details }
  1184.             if T <> 'O' then // do not owerwrite Other type, but create a new record instead 
  1185.               case MessageDlg('(TODO: add wizard here) Contact ' + F + ' already exists. Do you want to replace its current number [' +
  1186.                 contact^.pnumb + '] with imported number [' + N + '] ?'#13#13'(Click No to add it as a New contact)',
  1187.                 mtConfirmation,[mbYes,mbNo,mbCancel],0) of
  1188.                 mrYes: begin
  1189.                   //SyncLog(F + ' modified in FMA by Import.');
  1190.                   Modified := True;
  1191.                 end;
  1192.                 //mrNo: SyncLog(F + ' added in FMA by Import (as dublicate).');
  1193.                 mrCancel: Abort;
  1194.               end;
  1195.             //else SyncLog(F + ' added to FMA by Import.');
  1196.           end;
  1197.           //else SyncLog(F + ' added to FMA by Import.');
  1198.           if not Modified then begin
  1199.             Node := ListNumbers.AddChild(nil);
  1200.             contact := ListNumbers.GetNodeData(Node);
  1201.             contact^.position := FindFreePos;
  1202.             contact^.ptype := T;
  1203.           end;
  1204.           contact^.cname := F;
  1205.           contact^.pnumb := N;
  1206.           if Modified then begin
  1207.             contact^.imageindex := 1;
  1208.             inc(mods);
  1209.           end
  1210.           else begin
  1211.             contact^.imageindex := 0;
  1212.             inc(adds);
  1213.           end;
  1214.         end;
  1215.         ListNumbers.Update;
  1216.       end;
  1217.     finally
  1218.       sl.free;
  1219.       if (adds <> 0) or (mods <> 0) then begin
  1220.         //ListContacts.EndUpdate;
  1221.         ListNumbers.Sort(nil, ListNumbers.Header.SortColumn, ListNumbers.Header.SortDirection);
  1222.         Form1.UpdateSMPhonebook;
  1223.         Form1.Debug('Imported '+IntToStr(adds+mods)+' item(s)... ('+IntToStr(adds)+' added, '+IntToStr(mods)+' modified)');
  1224.       end;
  1225.     end;
  1226.   finally
  1227.     VCard.Free;
  1228.     FreeProgressDialog;
  1229.     //SyncLog('Import finished');
  1230.     Form1.Status('Import complete.');
  1231.   end;
  1232. end;
  1233.  
  1234. function TfrmSIMEdit.FindContact(FullName: WideString;
  1235.   var AContact: PSIMData): boolean;
  1236. var
  1237.   Node :PVirtualNode;
  1238. begin
  1239.   Result := False;
  1240.   Node := ListNumbers.GetFirst;
  1241.   while Node <> nil do
  1242.   try
  1243.     AContact := ListNumbers.GetNodeData(Node);
  1244.     if WideCompareText(FullName,AContact^.cname) = 0 then begin
  1245.       Result := True;
  1246.       break;
  1247.     end;
  1248.   finally
  1249.     Node := ListNumbers.GetNext(Node);
  1250.   end;
  1251. end;
  1252.  
  1253. function TfrmSIMEdit.FindContact(FullName: WideString;
  1254.   var ANode: PVirtualNode): boolean;
  1255. var
  1256.   Node :PVirtualNode;
  1257.   AContact: PSIMData;
  1258. begin
  1259.   Result := False;
  1260.   Node := ListNumbers.GetFirst;
  1261.   while Node <> nil do
  1262.   try
  1263.     AContact := ListNumbers.GetNodeData(Node);
  1264.     if WideCompareText(FullName,AContact^.cname) = 0 then begin
  1265.       ANode := Node;
  1266.       Result := True;
  1267.       break;
  1268.     end;
  1269.   finally
  1270.     Node := ListNumbers.GetNext(Node);
  1271.   end;
  1272. end;
  1273.  
  1274. function TfrmSIMEdit.FindContact(FullName, NumberType: WideString;
  1275.   var AContact: PSIMData): boolean;
  1276. var
  1277.   Node :PVirtualNode;
  1278. begin
  1279.   Result := False;
  1280.   Node := ListNumbers.GetFirst;
  1281.   while Node <> nil do
  1282.   try
  1283.     AContact := ListNumbers.GetNodeData(Node);
  1284.     if (WideCompareText(FullName,AContact^.cname) = 0) and
  1285.       (WideCompareText(NumberType,AContact^.ptype) = 0) then begin
  1286.       Result := True;
  1287.       break;
  1288.     end;
  1289.   finally
  1290.     Node := ListNumbers.GetNext(Node);
  1291.   end;
  1292. end;
  1293.  
  1294. procedure TfrmSIMEdit.ExportList(FileType: Integer; Filename: WideString);
  1295. var
  1296.   node: PVirtualNode;
  1297.   contact: PSIMData;
  1298.   AContact: TContactData;
  1299.   PContact: PContactData;
  1300.   sl: TStringList;
  1301.   str: WideString;
  1302.   VCard: TVCard;
  1303.   i: integer;
  1304.   s: string;
  1305.   XML: TXML;
  1306.   CurNodeName: string;
  1307. begin
  1308.   { TODO: export all records/numbers for a contact at once in a vCard file }
  1309.   PContact := @AContact;
  1310.   case FileType of
  1311.     1:begin//vCard
  1312.         VCard := TVCard.Create;
  1313.         sl := TStringList.Create;
  1314.         try
  1315.           with ListNumbers do begin
  1316.             node := GetFirst;
  1317.             if node <> nil then
  1318.             repeat
  1319.                try
  1320.                   if Selected[node] then begin
  1321.                      contact := GetNodeData(node);
  1322.                      FillChar(AContact,SizeOf(AContact),0);
  1323.                      SetContactFullName(PContact,contact^.cname);
  1324.                      if contact^.ptype = 'W' then PContact^.work := contact^.pnumb else
  1325.                      if contact^.ptype = 'H' then PContact^.home := contact^.pnumb else
  1326.                      if contact^.ptype = 'F' then PContact^.fax := contact^.pnumb else
  1327.                      if contact^.ptype = 'O' then PContact^.other := contact^.pnumb else
  1328.                        PContact^.cell := contact^.pnumb;
  1329.                      Contact2vCard(PContact,VCard);
  1330.                      sl.Clear;
  1331.                      sl.AddSTrings(VCard.Raw);
  1332.                      if ListNumbers.SelectedCount <> 1 then begin
  1333.                        str := contact^.ptype;
  1334.                        if str = 'O' then str := ''; // hide Other type
  1335.                        if str <> '' then str := ' ('+str+')';
  1336.                        str := Trim(contact^.cname) + str;
  1337.                        str := StringReplace(str,' ','-',[rfReplaceAll]);
  1338.                        str := ChangeFileExt(FileName,'-'+str)+ExtractFileExt(Filename);
  1339.                      end
  1340.                      else
  1341.                        str := Filename;
  1342.                      i := 0; s := str;
  1343.                      while FileExists(s) do begin
  1344.                        inc(i);
  1345.                        s := ChangeFileExt(str,'') + ' (' + IntToStr(i) + ')' + ExtractFileExt(str);
  1346.                      end;
  1347.                      sl.SaveToFile(s);
  1348.                   end;
  1349.                except
  1350.                end;
  1351.                node := GetNext(node);
  1352.             until node = nil;
  1353.           end;
  1354.         finally
  1355.           sl.Free;
  1356.           VCard.Free;
  1357.         end;
  1358.       end;
  1359.  
  1360.     2:begin//xml
  1361.         XML := TXML.Create();
  1362.         XML.TagName := 'fma_contacts';
  1363.  
  1364.         try
  1365.           with ListNumbers do
  1366.           begin
  1367.             node := GetFirst;
  1368.             if node <> nil then
  1369.             repeat
  1370.                try
  1371.                   if Selected[node] then
  1372.                   begin
  1373.                      contact := GetNodeData(node);
  1374.                      FillChar(AContact,SizeOf(AContact),0);
  1375.                      SetContactFullName(PContact,contact^.cname);
  1376.  
  1377.                      if contact^.ptype = 'W' then CurNodeName := 'work' else
  1378.                      if contact^.ptype = 'H' then CurNodeName := 'home' else
  1379.                      if contact^.ptype = 'F' then CurNodeName := 'fax' else
  1380.                      if contact^.ptype = 'O' then CurNodeName := 'other' else
  1381.                        CurNodeName := 'cell';
  1382.  
  1383.                      with XML.AddChild('contact') do
  1384.                      begin
  1385.                       AddChild('name', HTMLEncode(UTF8Encode(PContact^.name), False));
  1386.                       AddChild(CurNodeName, HTMLEncode(UTF8Encode(contact^.pnumb), False));
  1387.                       AddChild('position', HTMLEncode(UTF8Encode(IntToStr(contact^.position)), False));
  1388.                      end;
  1389.                   end;
  1390.                except
  1391.                end;
  1392.                node := GetNext(node);
  1393.             until node = nil;
  1394.           end;
  1395.  
  1396.           XML.Save(FileName);
  1397.         finally
  1398.           XML.Free();
  1399.         end;
  1400.       end;
  1401.     else
  1402.       raise Exception.Create('Not implemented yet');
  1403.   end;
  1404. end;
  1405.  
  1406. procedure TfrmSIMEdit.DownloadEntirePhonebook1Click(Sender: TObject);
  1407. begin
  1408.   if MessageDlg('Local Phonebook will be replaced with a fresh copy from the phone.'#13#13+
  1409.     'Any local changes will be lost. Do you wish to continue?',
  1410.     mtConfirmation,[mbYes,mbNo],0) = ID_YES then begin
  1411.       ListNumbers.Clear;
  1412.       FullRefresh;
  1413.     end;
  1414. end;
  1415.  
  1416. procedure TfrmSIMEdit.ListNumbersKeyDown(Sender: TObject; var Key: Word;
  1417.   Shift: TShiftState);
  1418. begin
  1419.   if (Key = VK_RETURN) and (ListNumbers.SelectedCount = 1) then
  1420.     btnEditClick(nil);
  1421. end;
  1422.  
  1423. procedure TfrmSIMEdit.FullRefresh;
  1424. begin
  1425.   Form1.ActionContactsDownload.Execute;
  1426.   UpdatePhonebook;
  1427. end;
  1428.  
  1429. procedure TfrmSIMEdit.FormStorage1SavePlacement(Sender: TObject);
  1430. var
  1431.   s: string;
  1432.   i: integer;
  1433. begin
  1434.   with ListNumbers.Header do begin
  1435.     s := IntToStr(SortColumn)+','+IntToStr(Ord(SortDirection));
  1436.     for i := 0 to Columns.Count-1 do
  1437.       s := s+','+IntToStr(Columns[i].Width)+','+IntToStr(Columns[i].Position);
  1438.   end;
  1439.   FormStorage1.StoredValue['ListHeader'] := s;
  1440. end;
  1441.  
  1442. procedure TfrmSIMEdit.FormStorage1RestorePlacement(Sender: TObject);
  1443. var
  1444.   s: widestring;
  1445.   i: integer;
  1446. begin
  1447.   s := FormStorage1.StoredValue['ListHeader'];
  1448.   if s <> '' then
  1449.     try
  1450.       with ListNumbers.Header do begin
  1451.         SortColumn := StrToInt(GetFirstToken(s));
  1452.         SortDirection := TSortDirection(StrToInt(GetFirstToken(s)));
  1453.         for i := 0 to Columns.Count-1 do begin
  1454.           Columns[i].Width := StrToInt(GetFirstToken(s));
  1455.           Columns[i].Position := StrToInt(GetFirstToken(s));
  1456.         end;
  1457.       end;
  1458.     except
  1459.     end;
  1460. end;
  1461.  
  1462. procedure TfrmSIMEdit.ListNumbersHeaderMouseUp(Sender: TVTHeader;
  1463.   Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  1464. begin
  1465.   FormStorage1SavePlacement(nil);
  1466. end;
  1467.  
  1468. end.
  1469.  
  1470.